package com.zzyl.job;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.zzyl.common.constant.CacheConstants;
import com.zzyl.common.utils.TimeRangeUtils;
import com.zzyl.nursing.domain.AlertData;
import com.zzyl.nursing.domain.AlertRule;
import com.zzyl.nursing.domain.Device;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.service.IAlertDataService;
import com.zzyl.nursing.service.IAlertRuleService;
import com.zzyl.nursing.service.IDeviceService;
import com.zzyl.nursing.ws.WebSocketServer;
import com.zzyl.system.service.ISysRoleService;
import io.jsonwebtoken.lang.Collections;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @Description: 报警数据处理任务
 * @Author: zzyl
 * @Date: 2020/7/27 14:05
 */
@Component
@Slf4j
public class AlertDataJob {
    @Autowired
    private IAlertRuleService alertRuleService;
    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;
    @Autowired
    IAlertDataService alertDataService;
    @Autowired
    private ISysRoleService sysRoleService;
    @Autowired
    private IDeviceService deviceService;
    @Autowired
    private WebSocketServer webSocketServer;
    public void processAlertData() {
        log.info("第一步：查询所有报警规则");
        //Status 1:启用 0:停用
        List<AlertRule> list = alertRuleService.list(new LambdaQueryWrapper<AlertRule>()
                .eq(AlertRule::getStatus, 1));
        if (Collections.isEmpty(list)){
            log.info("\t没有启用的报警规则");
            return;
        }
        log.info("第二步：遍历报警规则，根据规则查询其关联的产品设备");
        for (AlertRule rule : list) {
            log.info("\t报警规则:{}", rule);
            String iotId = rule.getIotId();
            //-1 表示所有
            List<Device> deviceList;
            if ("-1".equals(iotId)) {
                deviceList = deviceService.list(new LambdaQueryWrapper<Device>()
                        .eq(Device::getProductKey, rule.getProductKey()));
            } else {
                deviceList = deviceService.list(new LambdaQueryWrapper<Device>().eq(Device::getIotId, iotId));
            }
            if (Collections.isEmpty(deviceList)){
                log.info("\t当前规则下，没有关联的产品设备");
                return;
            }
            log.info("第三步：处理产品设备的数据");
            for (Device device : deviceList) {
                log.info("\t当前处理的设备：:{}", device.getDeviceName());
                //获取当前产品设备下的数据
                String str = (String) redisTemplate.opsForHash().get(CacheConstants.IOT_DEVICE_LAST_DATA, device.getIotId());
                if (StrUtil.isNotEmpty(str)){
                    List<DeviceData> dataList = JSONUtil.toList(str, DeviceData.class);
                    String functionId = rule.getFunctionId();
                    for (DeviceData data : dataList) {
                        if (functionId.equals(data.getFunctionId())){
                            log.info("\t当前处理的数据：:{}", data);
                            handlerData(rule, data);
                        };
                    }




                }
            }
        }

    }
    /**
     * 处理数据
     * @param rule 报警规则
     * @param data 设备数据
     */
    private void handlerData(AlertRule rule, DeviceData data) {
        //1.判断数据上报时间是否超过1分钟
        log.info("===============1、是超过1分钟，不处理");

        if(data.getAlarmTime().isBefore(LocalDateTime.now().minusMinutes(1))){
            log.info("\t数据上报时间超过1分钟，不处理");
            return;
        }
        //2.判断数据上报时间是否在报警生效时间段范围内，不在则不处理
        // 10:00:00-12:00:00
        String alertEffectiveTime = rule.getAlertEffectivePeriod();
        log.info("===============2、判断数据上报时间是否在报警生效时间段内");
        if (!TimeRangeUtils.isCurrentTimeInRange(alertEffectiveTime)) {
            log.info("\t数据上报时间不在报警生效时间段内，不处理");
            return;
        }
        //3.判断此次上报数据是否触发告警   100 >= 55
        String Gongshi = data.getDataValue() + rule.getOperator() + rule.getValue();
        log.info("===============3、判断此次上报数据是否触发告警");
        boolean result = evaluateAlertCondition(Gongshi);
        log.info("\t计算公式是：{}，是否触发告警:{}", Gongshi, result);
        String countkey = CacheConstants.ALERT_TRIGGER_COUNT.replace("{IotId}", data.getIotId())
                .replace("{FunctionId}", data.getFunctionId())
                .replace("{RuleId}", String.valueOf(rule.getId()));
        if (!result){
            log.info("\t数据不满足报警条件，不处理");
            log.info("\t删除连续累计报警数据:{}",countkey);
            redisTemplate.delete(countkey);
            return;
        }
        //4.判断当前设备是否处于报警沉默周期，如果还在沉默周期内，则不处理
        String Slilectkey = CacheConstants.ALERT_SILENCE.replace("{IotId}", data.getIotId())
                .replace("{FunctionId}", data.getFunctionId())
                .replace("{RuleId}", String.valueOf(rule.getId()));
        log.info("===============4、判断当前设备是否处于报警沉默周期");
        if (Boolean.TRUE.equals(redisTemplate.hasKey(Slilectkey))){
            log.info("\t当前设备处于报警沉默周期，不处理");
            return;
        }
        //5.判断连续报警次数是否超过阈值，有或没有都连续报警次数加1
        long count = redisTemplate.opsForValue().increment(countkey);
        log.info("\t连续累计异常次数:{}", count);
        if (count > rule.getDuration()) {
            log.info("===============5、判断连续报警次数是否超过阈值");
            log.info("\t连续报警次数{}超过阈值{}，触发报警", count, rule.getDuration());
            //5.1.触发报警
            log.info("\t触发报警");



            //5.2.删除Redis中连续累计异常次数
            log.info("\t删除连续累计异常次数:{}", countkey);
            redisTemplate.delete(countkey);
            //5.3存储沉默周期到Redis
            log.info("\t存储沉默周期到Redis:{}", Slilectkey);
            redisTemplate.opsForValue().set(Slilectkey, data, rule.getAlertSilentPeriod(), TimeUnit.MINUTES);
            //5.4报警数据写入数据库
            log.info("\t报警数据写入数据库");

            List<Long> userIds = getAlertReceiver(rule,data);
            if (CollUtil.isEmpty(userIds)){
                log.info("\t没有报警接收人，不处理");
                return;
            }
            List<AlertData> alertDataList = userIds.stream().map(userId -> {
                        AlertData alertData = new AlertData();
                        alertData.setIotId(data.getIotId());
                        alertData.setDeviceName(data.getDeviceName());
                        alertData.setProductKey(data.getProductKey());
                        alertData.setProductName(rule.getProductName());
                        alertData.setFunctionId(data.getFunctionId());

                        alertData.setAccessLocation(data.getAccessLocation());
                        alertData.setLocationType(data.getLocationType());
                        alertData.setPhysicalLocationType(data.getPhysicalLocationType());

                        alertData.setDeviceDescription(data.getDeviceDescription());
                        alertData.setDataValue(data.getDataValue());

                        alertData.setAlertRuleId(rule.getId());

                        alertData.setAlertReason(rule.getFunctionName()+"连续" + rule.getDuration() + "次" + rule.getOperator() + rule.getValue());
                        alertData.setType(rule.getAlertDataType());
                        //0 待处理，1已处理
                        alertData.setStatus(0);

                        alertData.setUserId(userId);
                        return alertData;
                    }).collect(Collectors.toList());
            alertDataService.saveBatch(alertDataList);
            log.info("\t报警数据:{}", data);
            //6.发送报警消息给接收人
            log.info("\t发送报警消息给接收人");
            webSocketServer.sendMessageToHandler(userIds, data,rule);
        }


    }
    /**
     * 获取报警接收人
     * @param rule 报警规则
     * @param data 设备数据
     * @return 报警接收人ID列表
     */
    private List<Long> getAlertReceiver(AlertRule rule, DeviceData data) {
        List<Long> userIds = new ArrayList<>();
        if (rule.getAlertDataType() == 0){
            //老人异常数据: 获取该设备关联的老人绑定的护工
            List<Long> nursingIds = deviceService.getNursingIdByyiotId(data.getIotId());
            userIds.addAll(nursingIds);

        }else {
            //设备异常数据
            List<Long> ids = sysRoleService.getUserIdsByRoleNameOrRoleKey("维修工");
            userIds.addAll(ids);
        }
        List<Long> superIds = sysRoleService.getUserIdsByRoleNameOrRoleKey("超级管理员");
        userIds.addAll(superIds);
        return userIds;
    }

    /**
     * 计算报警条件表达式
     * @param expression 报警条件表达式，如 "100 >= 55"
     * @return 是否满足条件
     */
    private boolean evaluateAlertCondition(String expression) {
        if (StrUtil.isEmpty(expression)) {
            return false;
        }

        try {
            // 使用ScriptEngine计算表达式
            javax.script.ScriptEngineManager manager = new javax.script.ScriptEngineManager();
            javax.script.ScriptEngine engine = manager.getEngineByName("js");
            Object result = engine.eval(expression);

            // 返回布尔结果
            if (result instanceof Boolean) {
                return (Boolean) result;
            } else {
                // 如果是数值结果，判断是否为true（非0）
                return Double.parseDouble(result.toString()) != 0;
            }
        } catch (Exception e) {
            log.error("报警条件计算异常，expression: {}", expression, e);
            return false;
        }
    }

}
